package edu.xidian.cnmano.service.impl;

import edu.xidian.cnmano.dao.NsinstanceMapper;
import edu.xidian.cnmano.entities.nsdmanagement.Virtualizednetworkfunctiondescriptor;
import edu.xidian.cnmano.entities.nsorchestrate.Nsinstance;
import edu.xidian.cnmano.entities.nsorchestrate.NsinstanceExample;
import edu.xidian.cnmano.entities.nsorchestrate.Vnfinstance;
import edu.xidian.cnmano.requestbody.ScaleNsRequest;
import edu.xidian.cnmano.requestbody.ScaleVnfData;
import edu.xidian.cnmano.service.NsinstanceService;
import edu.xidian.cnmano.service.VnfinstanceService;
import edu.xidian.cnmano.utils.RestTemplateUtil;
import edu.xidian.cnmano.utils.SystemCallUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zhr
 * @date 2021/1/15-15:01
 */
@Service
@Slf4j
public class NsinstanceServiceImpl implements NsinstanceService {
    @Resource
    NsinstanceMapper mapper;

    @Resource
    VnfinstanceService vnfinstanceService;

    @Override
    public List<Nsinstance> listNsinstance() {
        NsinstanceExample example = new NsinstanceExample();
        return mapper.selectByExample(example);
    }

    /**
     * 前端提交的信息只有nsInstanceName，nsInstanceDescription，nsdId
     * 需要补充的有vnfinstance和NsState
     * 因此需要根据nsdId获取nsd，之后根据nsd中的多个vnfdId，分别创建vnfinstance，
     * 具体的创建vnfinstance可以直接交给vnfinstanceService来做
     * vnfinstance创建前还根据vnfdId查询出相应的vnfd，之后用vnfd中的一些信息来补充
     * 因为没有前端信息提交而导致的vnfinstance中的vnfInstanceName，vnfInstanceDescription，vnfdId，flavourId信息空缺
     *
     * @param nsinstance
     * @return
     */
    @Override
    public int createNsinstace(Nsinstance nsinstance) {
        nsinstance.setNsState("NOT_INSTANTIATED");
        Integer nsdId = nsinstance.getNsdId();
        //得到vnfdId字符串数组
        String[] vnfdIdStrArray = RestTemplateUtil.doGetNsdById(nsdId).getVnfdId().split(",");
        List<String> vnfinstancesIdList = new ArrayList<>();
        //通过vnfId查询到相应的vnfd
        for (String vnfdIdStr : vnfdIdStrArray) {
            Vnfinstance vnfinstance = new Vnfinstance();
            Integer vnfdId = Integer.parseInt(vnfdIdStr);
            Virtualizednetworkfunctiondescriptor vnfd = RestTemplateUtil.doGetVnfdById(vnfdId);
            vnfinstance.setVnfdId(vnfd.getVnfdId());
            vnfinstance.setFlavourId(vnfd.getDeploymentFlavour());
            vnfinstance.setVnfInstanceDescription("vnfInstance-inside-" + nsinstance.getNsInstanceDescription());
            vnfinstance.setVnfInstanceName(vnfd.getVnfProductName() + "-" + vnfd.getVnfdVersion() + "-instance");
            int vnfInstanceId = vnfinstanceService.createVnfinstance(vnfinstance);
            vnfinstancesIdList.add(vnfInstanceId + "");
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < vnfinstancesIdList.size(); i++) {
            sb.append(vnfinstancesIdList.get(i));
            if (i < vnfinstancesIdList.size() - 1) {
                sb.append(",");
            }
        }
        nsinstance.setVnfInstance(sb.toString());
        return mapper.insert(nsinstance);
    }

    @Override
    public int instantiateNsinstace(Integer id) {
        Nsinstance nsinstance = mapper.selectByPrimaryKey(id);
        if (nsinstance.getNsState().equals("INSTANTIATED")) {
            return 1;
        }

        //提前创建namespace，为后面vnfinstance实例化做准备
        String command = "bash /home/k8s-mec/cnMANO/scripts/beforeInstantiate.sh " + nsinstance.getNsInstanceName();
        SystemCallUtil.exec(command);

        String[] vnfInstanceArray = nsinstance.getVnfInstance().split(",");
        for (String VnfInstanceId : vnfInstanceArray) {
            //实例化vnfinstance的时候需要传入NsInstanceName()，作为k8s的名称空间
            vnfinstanceService.instantiateVnfinstance(Integer.parseInt(VnfInstanceId), nsinstance.getNsInstanceName());
        }
        //所有VNFinstance实例化完成之后，nsinstance也就实例化完成了
        Nsinstance toUpdate = new Nsinstance();
        toUpdate.setId(id);
        toUpdate.setNsState("INSTANTIATED");
        return mapper.updateByPrimaryKeySelective(toUpdate);
    }

    @Override
    public int scaleNsinstace(ScaleNsRequest scaleNsRequest) {
        String[] scaleVnfDatas = scaleNsRequest.getScaleVnfData().split(",");
        for(int i=0;i<scaleVnfDatas.length;i+=2){
            ScaleVnfData scaleVnfData = new ScaleVnfData();
            scaleVnfData.setVnfInstanceId(Integer.parseInt(scaleVnfDatas[i]));
            scaleVnfData.setVnfcTargetNumber(Integer.parseInt(scaleVnfDatas[i+1]));
            vnfinstanceService.scaleVnfinstance(scaleVnfData,mapper.selectByPrimaryKey(scaleNsRequest.getNsInstanceId()).getNsInstanceName());
        }
        return 1;
    }

    @Override
    public int terminateNsinstace(Integer id) {
        Nsinstance nsinstance = mapper.selectByPrimaryKey(id);
        //如果已经是终止状态，就不需要重新终止了
        if (nsinstance.getNsState().equals("NOT_INSTANTIATED")) {
            return 1;
        }

        String[] vnfInstanceArray = nsinstance.getVnfInstance().split(",");
        for (String VnfInstanceId : vnfInstanceArray) {
            vnfinstanceService.terminateVnfinstance(Integer.parseInt(VnfInstanceId), nsinstance.getNsInstanceName());
        }

        long startTime = System.currentTimeMillis();
        //所有VNFinstance终止完成之后，nsinstance也就终止了，先删除名称空间，然后更新ns状态
        String command = "bash /home/k8s-mec/cnMANO/scripts/afterTerminate.sh " + nsinstance.getNsInstanceName();
        SystemCallUtil.exec(command);
        long endTime = System.currentTimeMillis();
        log.info("删除名称空间{}，占用时间{}ms",nsinstance.getNsInstanceName(),endTime-startTime);

        Nsinstance toUpdate = new Nsinstance();
        toUpdate.setId(id);
        toUpdate.setNsState("NOT_INSTANTIATED");
        return mapper.updateByPrimaryKeySelective(toUpdate);
    }


    @Override
    public int deleteNsinstace(Integer id) {
        Nsinstance nsinstance = mapper.selectByPrimaryKey(id);
        String[] vnfInstanceArray = nsinstance.getVnfInstance().split(",");
        for (String vnfInstanceStr : vnfInstanceArray) {
            vnfinstanceService.deleteVnfinstance(Integer.parseInt(vnfInstanceStr));
        }
        return mapper.deleteByPrimaryKey(id);
    }
}
